package client;

import exception.AddOrderException;
import exception.AuthClientException;
import exception.RemoveClientException;
import marketdepth.MarketDepth;

import java.io.IOException;
import java.net.Socket;
import java.net.SocketException;
import java.util.logging.Logger;

/**
 * Class fully responsible for communicating with client computer
 */
public class Client implements Runnable {
    private static Logger log = Logger.getLogger(Client.class.getName());

    private String name;
    private final StringSocket clientSocket;
    private final MarketDepth marketDepth;

    public Client(Socket connection, MarketDepth marketDepth)
            throws IOException {
        this.clientSocket = new StringSocket(connection);
        this.marketDepth = marketDepth;
    }

    @Override
    public void run() {
        while (true) {
            try {
                String lastMessage = receiveMessage();
                MessageHandler.authClient(lastMessage, this, marketDepth);
                sendAuthSuccessfully();
                break;
            } catch (AuthClientException e) {
                log.info("Auth Error: " + e.getMessage());
                sendAuthError(e.getMessage());
            } catch (IOException e) {
                log.info("Error receive message from client: " +
                        e.getMessage());
                sendAddedError("Error receive message from client: "
                        + e.getMessage());
            }
        }

        while (true) {
            try {
                String lastMessage = receiveMessage();
                long orderId = MessageHandler.addOrder(lastMessage, this,
                        marketDepth);
                sendAddedSuccessfully(orderId);
            } catch (AddOrderException e) {
                log.info("Send Error: " + e.getMessage());
                sendAddedError(e.getMessage());
            } catch (SocketException e) {
                log.info("Error Socket (remove client): " + e.getMessage());
                try {
                    MessageHandler.removeClient(this, marketDepth);
                } catch (RemoveClientException e2) {
                    log.info("Error remove client: " + e2.getMessage());
                }
            } catch (IOException e) {
                log.info("Error receive message from client: " +
                        e.getMessage());
                sendAddedError("Error receive message from client: "
                        + e.getMessage());
            }
        }
    }

    /**
     * Method of obtaining message
     * @return accepted message
     * @throws IOException
     */
    public String receiveMessage() throws IOException{
        String message = clientSocket.read();
        log.info("Receive message: " + message);
        return message;
    }

    public void sendMessage(String message) throws IOException {
        clientSocket.write(message);
    }


    /**
     * Method of sending a filled reports with details
     * @param orderId id filled order in the trade exchange
     * @param transactionSize number of shares of filled order
     */
    public void sendFilledData(long orderId, int transactionSize) {
        String message = orderId + " " + transactionSize;
        MessageHandler.sendMessage(MessagePrefix.FILLED, message, this);
    }

    /**
     * Method of sending a message about the successful adding of the order
     * @param orderId id added order in the trade exchange
     */
    public void sendAddedSuccessfully(long orderId) {
        String message = String.valueOf(orderId);
        MessageHandler.sendMessage(MessagePrefix.ADDED, message, this);
    }

    /**
     * Method of sending a message about the successful authorization
     */
    public void sendAuthSuccessfully() {
        MessageHandler.sendMessage(MessagePrefix.AUTHOK, this);
    }

    /**
     * Method of sending a message error of authorization
     * @param errorMessage message error of authorization
     */
    public void sendAuthError(String errorMessage) {
        MessageHandler.sendMessage(MessagePrefix.ERRAUTH, errorMessage, this);
    }

    /**
     * Method of sending a message error of adding order
     * @param errorMessage message error of adding order
     */
    public void sendAddedError(String errorMessage) {
        MessageHandler.sendMessage(MessagePrefix.ERRADD, errorMessage, this);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
